package com.zbj.homemade.spring;

import com.zbj.homemade.spring.annotation.Component;
import com.zbj.homemade.spring.beans.BeanDefinition;
import com.zbj.homemade.spring.beans.DefaultSingletonBeanRegistry;
import com.zbj.homemade.spring.beans.FactoryBean;
import com.zbj.homemade.spring.beans.PropertyValue;
import com.zbj.homemade.spring.exception.BeansException;
import com.zbj.homemade.spring.lifecycle.BeanPostProcessor;
import com.zbj.homemade.spring.lifecycle.InitializingBean;
import com.zbj.homemade.spring.utils.Assert;
import com.zbj.homemade.spring.utils.CollectionsUtil;
import com.zbj.homemade.spring.utils.NameUtil;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


/**
 * |                    .  ’   *   ‘  .
 * |               _6                    "     o0000o
 * |  o00o _    .%                         %. o0000000o
 * | o000000o.:                              :(0000000o
 * | o00000os                                  .000000o
 * |  o0000/     <b>不了不了,改需求真的伤身体</>   .000o
 * |    o0o                                       .0o
 * |     y        <b>我认为源代码,就是SkrSkr</>     .
 * |    y                                           ,
 * |   y          <b>小熊猫保佑,代码永不修改</>        ,
 * |   p                            .                 .
 * |   p                           @@.                .
 * |    y                        ;@@@;               .
 * |    \                       ;@@@@;.@.          ,;S
 * |     、                     .@@@@@@;       ,@@@@@
 * |     @\.            .****...*@@@@;     .@@@@@@@@@@@.
 * |    @@@@\.     . @@@@@@@@@@@@@@@;    .@@@@@@@@@@@@@@@.
 * |   @@@@@@@@@.@@@@@@@@@@@@@@@@;   .@@@@@@@@@@@@@@@@@@@@@.
 * |  @@@@@@..@@@@..@@@@@@..@@@@@ .@@...@@@@@..@@..@@@@@..@@.
 * | @@@@@@@@..@..@@@@@@@@@@@@@;.@@@..@..@@@..@@....@@@..@@@@.
 * |@y@@@@@@@@..@@@@@@@@..@@@@i@@@@..@@@..@..@@..@@..@..@@@@n@.
 * |@n@@@@@@@..@@@@@@@@..@@@@@@@@@..@@@@@...@@..@@@@...@@@@@n@@.
 * |@@@@@@@@..@@@@@@@@..@@@@@@@@@..@@@@@@@.@@..@@@@@..@@@@@@@@@@.
 * |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
 *
 * @program: principle
 * @description:
 * @author: Yinn
 * @create: 2019-02-28 16:19
 **/
public abstract class AbstractBeanFatory extends DefaultSingletonBeanRegistry implements BeanFactory {

    private final List<BeanPostProcessor> processors = new ArrayList<>();

    private final ConcurrentMap<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(256);

    protected abstract List<String> getBeanNamesByType(Class<?> cls);
    /**
     * ★与spring方式不同
     */
    @Override
    public <T> T getBean(Class<T> cls) {

        String name;
        if (cls.isInterface()){
            List<String> beanNamesByType = getBeanNamesByType(cls);

            if (CollectionsUtil.isEmpty(beanNamesByType)){
                throw new BeansException("Class type is" + cls.getName() + " ,cannot find the bean!");
            }
            if (beanNamesByType.size() > 1){
                throw new BeansException("Class type is" + cls.getName() + " ,不知道改注入哪个bean!共找到:" + beanNamesByType.size());
            }

            name = beanNamesByType.get(0);
        }else {
            name = NameUtil.toLowerCaseFirstOne(cls.getSimpleName());
            Component component = cls.getAnnotation(Component.class);
            if (component != null && StringUtils.isNotBlank(component.name())) {
                name = component.name();
            }
        }

        return (T) this.getBean(name);
    }

    @Override
    public Object getBean(String beanName) {

        Assert.notEmpty(beanName, "bean名称不可为空");

        Object bean = getSingleton(beanName);

        if (bean != null) {

            Object factoryMade = factoryGet(bean, beanName);

            if (factoryMade != null)
                return factoryMade;
        }

        BeanDefinition bd = getBeanDefinition(beanName);

        if (bd == null) {
            throw new BeansException(beanName + "未找到对应定义！");
        }

        return createBean(beanName, bd);
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor postProcessor) {

        if (postProcessor != null) {
            processors.remove(postProcessor);
            processors.add(postProcessor);
        }

    }


    protected Object factoryGet(Object beanInstance, String beanName) {

        if (beanInstance == null)
            return null;

        if (!(beanInstance instanceof FactoryBean))
            return beanInstance;

        Object factoryMade = factoryBeanObjectCache.get(beanName);

        if (factoryMade == null) {

            FactoryBean factoryBean = (FactoryBean) beanInstance;

            Object bean = factoryBean.getObject();

            bean = initialization(bean, beanName);

            if (factoryBean.isSingleton()) {
                factoryBeanObjectCache.put(beanName, bean);
            }

            return bean;
        }

        return null;
    }

    /**
     * 赋值操作
     */
    protected Object setProperties(Object bean, BeanDefinition sbd){

        if (sbd.hasPropertyValue()){

            List<PropertyValue> pvs = sbd.propertyValues();

            for (PropertyValue pv: pvs){

                Field field = pv.getField();

                field.setAccessible(true);
                if (pv.isAutowired()){
                    try {
                        field.set(bean, getBean(pv.getAutowiredClass()));
                    } catch (IllegalAccessException e) {
                        System.out.println(bean.getClass());
                        e.printStackTrace();
                    }
                }

            }

        }

        return bean;

    }

    protected Object initialization(Object bean,String beanName) {

        //bean前-后置处理器
        bean = beforePostProcessor(bean,beanName);

        //InitlizationBean
        if (bean instanceof InitializingBean){
            InitializingBean ib = (InitializingBean) bean;
            try {
                ib.afterPropertiesSet();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //bean后-后置处理器
        bean = afterPostProcessor(bean,beanName);
        return bean;
    }

    protected Object beforePostProcessor(Object bean, String beanName) {

        Object result = bean;

        for (BeanPostProcessor postProcessor : this.getProcessors()) {

            if (result == null){
                result = postProcessor.postProcessBeforeInitialization(bean, beanName);
            }else {
                result = postProcessor.postProcessBeforeInitialization(result, beanName);
            }

        }

        if (result == null)
            return bean;

        return result;
    }

    protected Object afterPostProcessor(Object bean, String beanName) {

        Object result = bean;

        for (BeanPostProcessor postProcessor : this.getProcessors()) {

            if (result == null){
                result = postProcessor.postProcessAfterInitialization(bean, beanName);
            }else {
                result = postProcessor.postProcessAfterInitialization(result, beanName);
            }

        }

        if (result == null)
            return bean;

        return result;
    }



    protected abstract void preInstantiateSingletons();

    public List<BeanPostProcessor> getProcessors() {
        return processors;
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    protected abstract Object createBean(String beanName, BeanDefinition db) throws BeansException;

}
